/*
Copyright 2010 Peter Krajnc, SCREENWORKER

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.screenworker.compactmvc.core
{
	import com.screenworker.compactmvc.interfaces.IModel;
	import com.screenworker.compactmvc.patterns.Controller;
	import com.screenworker.compactmvc.vo.NoteVO;
	
	/**
	 * MVC Core
	 * @author Peter Krajnc
	 * 
	 */	
	public class CompactMVC
	{
		public static const VERSION: String 			= "1.0.4";
		public static const AUTHOR:String 				= "Peter Krajnc, SCREENWORKER";
		public static const SOURCE:String 				= "http://compactmvc.googlecode.com/svn/trunk/";
		public static const EXAMPLE_FLEX:String 		= "http://compactmvc-example-flex.googlecode.com/svn/trunk/";
		public static const EXAMPLE_FLASH:String 		= "http://compactmvc-example-flash.googlecode.com/svn/trunk/";
		public static const EXAMPLE_AIR:String 			= "http://compactmvc-example-air.googlecode.com/svn/trunk/";
		
		//Log Level
		public static const LOGLEVEL_ALL:String 		= 'all';
		public static const LOGLEVEL_DEBUG:String 		= 'debug';
		public static const LOGLEVEL_NONE:String 		= 'none';
		
		/*	DI Annotations:
			
		[Inject] initialize and removes the instance via DI (Dependency Injection)

		//todo
		Control of the life cycle ):
	    [Execute] executes the annotated method of a handler class, as soon as the handler is called
	    [PostConstruct] executes the annotated method after the instantiation is complete via DI is (typically use methods to create the UI)
	    [PreDestroy] executes the annotated method before the object is deleted
		*/
		public static const ANNOTATION_INJECT:String			= 'Inject';
		public static const ANNOTATION_EXECUTE:String			= 'Execute';
		public static const ANNOTATION_POST_CONSTRUCT:String	= 'PostConstruct';
		public static const ANNOTATION_PRE_DESTROY:String		= 'PreDestroy';
		
		//Singleton Instance
		private static var instance:CompactMVC;
		private var _core:Core;

		/**
		* Static accessor method.
		* @return A unique instance of this class.
		*/
		public static function getInstance():CompactMVC 
		{	
			if(!instance)
			{
				instance = new CompactMVC( new SingletonEnforcer() );
			}
			return instance;
			
		}
		
		/**
		 * constructor 
		 * @param enforcer Singleton enforcer
		 * 
		 */		
		public function CompactMVC(enforcer:SingletonEnforcer) 
		{
			if (!enforcer)
			{
				throw new Error('CompactMVC must be instantiated through CompactMVC.getInstance()');
			}
			_core = new Core();	
		}
		
      	/**
      	 * registers the view instance
      	 * @param controllerName
      	 * @param viewName
      	 * @param viewInstance
      	 * 
      	 */		
      	public function registerView(controllerName:String, viewName:String, viewInstance:Object):void
      	{    		
      		core.registerView(controllerName, viewName, viewInstance);
      	}
      	
      	/**
      	 * remove the registered view instance
      	 * @param controllerName
      	 * @param viewName
      	 * @param viewInstance
      	 * 
      	 */		
      	public function removeView(controllerName:String, viewName:String):void
      	{    		
      		core.removeView(controllerName, viewName);
      	}
		
		/**
		 * checks whether a view is present
		 * @param controllerName
		 * @param viewName
		 * @return 
		 * 
		 */		
		public function isViewRegistered(controllerName:String, viewName:String):Boolean
		{    		
			return core.isViewRegistered(controllerName, viewName);
		}
      	
		/**
		 * notifies one controller by name 
		 * @param controllerName
		 * @param notificationType
		 * @param data
		 * 
		 */      	
		public function sendNotificationTo(controllerName:String, notificationType:String, data:Object=null, delay:Number=0):void 
		{	
			core.retrieveController(controllerName).sendNotification( new NoteVO(notificationType, data), delay );
		}
		
		/**
		 * 
		 * @param notificationType
		 * @param data
		 * @param delay
		 * 
		 */		
		public function sendJunctionNotification(notificationType:String, data:Object=null, delay:Number=0):void
		{
			var controllers:Array = core.retrieveRegisteredControllers();
			for each(var item:Controller in controllers)
			{
				item.sendNotification( new NoteVO(notificationType, data), delay );
				//trace("sendJunctionNotification:" + item.getName() + " type:" + notificationType);
			}
		}
		
		/**
		 * returns the core Instance 
		 * @return 
		 * 
		 */	
		public function get core():Core
		{
			return _core;
		}
		
	}
}

import com.screenworker.compactmvc.core.CompactMVC;
import com.screenworker.compactmvc.interfaces.ICommand;
import com.screenworker.compactmvc.interfaces.IController;
import com.screenworker.compactmvc.interfaces.IModel;
import com.screenworker.compactmvc.patterns.Controller;
import com.screenworker.compactmvc.patterns.Model;
import com.screenworker.compactmvc.vo.ControllerVO;
import com.screenworker.compactmvc.vo.NoteVO;

import flash.display.DisplayObject;
import flash.net.getClassByAlias;
import flash.utils.describeType;
import flash.utils.getDefinitionByName;
import flash.utils.getQualifiedClassName;

import mx.charts.chartClasses.InstanceCache;

/**
 * The CompactMVC Core
 * @author Peter Krajnc, Screenworker
 * 
 */	
class Core
{
	private var _controller:Array = new Array();
	/**
	 * constructor 
	 * @param enforcer
	 * 
	 */		
	public function Core()
	{	
		
	}
	
	/**
	 * registered a controller 
	 * @param controller
	 * 
	 */		
	public function registerController(controller:Controller):void
	{
		if( isControllerRegistered(controller.getName()) )
		{
			throw new Error('controller already registered: ' + controller.getName());
		}  
		
		 _controller[ controller.getName() ] = new ControllerVO(controller);
		 controller.registerHandler();
	}
	
	/**
	 * removes a controller 
	 * @param controller
	 * 
	 */		
	public function removeController(controller:Controller):void
	{
		if( !isControllerRegistered(controller.getName()) )
		{
        	throw new Error('controller not registered: ' + controller.getName());
		}  
		_controller[  controller.getName()  ] = null;
		delete _controller[  controller.getName()  ];
  	}
  	
  	/**
  	 * retrieves a registered Controller by Name
  	 * @param controllerName
  	 * @return 
  	 * 
  	 */      	
  	public function retrieveController(controllerName:String):Controller
	{	
		if( !isControllerRegistered(controllerName) )
		{
			log(controllerName, 'controller not registered: ' + controllerName);
			return null;
		}
		return ControllerVO(_controller[ controllerName ]).controller;
	}
	
	/**
  	 * retrieves all registered Controllers 
  	 * @return 
  	 * 
  	 */      	
  	public function retrieveRegisteredControllers():Array
	{	
		var arr:Array = new Array();
		for each(var item:ControllerVO in _controller)
		{
			arr.push(item.controller);
		}
		return arr;
	}
	
	/**
	 * register command 
	 * @param controller
	 * @param notificationType
	 * @param commandClassRef
	 * 
	 */	
	public function registerCommand(controller:Controller, notificationType:String, commandClassRef:Class):void
	{
		if(ControllerVO(_controller[ controller.getName() ]).commands[ notificationType ] != null )
		{
			throw new Error('command already registered: ' + notificationType);
		}  
		ControllerVO(_controller[ controller.getName() ]).commands[notificationType] = commandClassRef;
	}
	
	/**
	 * removes a registered Command 
	 * @param controller
	 * @param notificationType
	 * 
	 */		
	public function removeCommand(controller:Controller, notificationType:String):void
	{
		if( ControllerVO(_controller[ controller.getName() ]).commands[notificationType] == null)
		{
        	throw new Error('command not registered: ' + notificationType);
		}  
		ControllerVO(_controller[ controller.getName() ]).commands[notificationType] = null;
		delete ControllerVO(_controller[ controller.getName() ]).commands[notificationType]; 	
  	}      
  	
  	/**
  	 * retrieves a registered command class reference
  	 * @param controller
  	 * @param notificationType
  	 * @return 
  	 * 
  	 */      	
  	public function retrieveCommand(controllerName:String, notificationType:String):Class
	{	
		if(controllerName  == null)
		{
			throw new Error( 'controllerName not given!');	
		}

		if(ControllerVO(_controller[ controllerName ]).commands[notificationType] == null)
		{
			log(controllerName, 'command for notificationtype "' +  notificationType +'" not registered!');	
				 
			return null;
		}
		return ControllerVO(_controller[ controllerName ]).commands[notificationType];
	}
	
	/**
	 * executes a command stateless and then removes the command
	 */  
	public function executeCommand(controllerName:String, note:NoteVO):void
	{
		var commandClassRef:Class = retrieveCommand(controllerName, note.type) as Class;
		if(commandClassRef == null)
			return;
			
		var commandInstance:ICommand = new commandClassRef( retrieveController(controllerName) );
		var controller:IController = retrieveController(controllerName);
		if(commandInstance && commandInstance is ICommand && controller && controller is IController)
		{	
			//dependency Injection
			propertyInjection(commandClassRef, _controller[ controllerName ], commandInstance);
			
			//execution
			commandInstance.execute(note);
			
			//destroy
			commandInstance = null;		
		}
	}
		
	/**
	 * register a model
	 * @param controller
	 * @param modelName
	 * @param modelClassRef
	 * 
	 */		
	public function registerModel(controller:Controller, modelName:String, modelClassRef:Class):void
  	{ 		
  		if(isModelRegistered(controller.getName(), modelName) )
		{	
			throw new Error('registerModel Error: model already registered: ' + modelName + ' modelClassRef:' +  getQualifiedClassName(modelClassRef));				
  		}
  		if(modelClassRef.NAME != modelName)
  		{
  			throw new Error('registerModel Error: name of modelClassRef did not match with modelName! The modelClassRef or modelName is wrong!: modelName: ' + modelName + ' modelClassRef:' +  getQualifiedClassName(modelClassRef));		
  		}
  		
  		var model:Object =  new modelClassRef(modelName, controller);		
  		if(model is Model && model is IModel)
  		{
  			ControllerVO(_controller[ controller.getName() ]).models[ modelName ] = model;
  			
  			//start dependency Injection   
       		dependencyInjection(_controller[ controller.getName() ] as ControllerVO);
  			
  			//call registration method
			ControllerVO(_controller[ controller.getName() ]).models[ modelName ].registerHandler();
			
			//notify Application that model has registered
			controller.sendNotification(new NoteVO(Controller.MODEL_REGISTER, null, modelName, model) );

  			log(controller.getName(), 'Model registered: ' +  modelName + ' ClassReference: ' + modelClassRef + ' to Controller: ' + controller.getName() );		
  		}
  		else
  		{
  			throw new Error('invalid modelClassRef!');
  		}
  	}
  	
  	/**
  	 * removes a registered Model 
  	 * @param controller
  	 * @param modelName
  	 * 
  	 */      	
  	public function removeModel(controller:Controller, modelName:String):void
  	{
  		if( ControllerVO(_controller[ controller.getName() ]).models[modelName] == null)
		{
        	throw new Error('model not registered: ' + modelName);
		}  
		
		//notify Application before model is removed
		controller.sendNotification( new NoteVO(Controller.MODEL_REMOVE, null, modelName, ControllerVO(_controller[ controller.getName() ]).models[modelName]) );	
	
		//call removeHandler
		IModel(ControllerVO(_controller[ controller.getName() ]).models[modelName]).removeHandler();
			
		//clean up all dependencies...
		var instance:Object = ControllerVO(_controller[ controller.getName() ]).models[modelName];
		var controllerVo:ControllerVO = _controller[ controller.getName() ] as ControllerVO;
		
		//... in registered views
		for each (var viewInstance:Object in controllerVo.views)
		{
			//start dependency rejection
			if(viewInstance && viewInstance.constructor)
			{
				propertyRejectionOfInstance(viewInstance.constructor, controllerVo, instance);
			}
		}	
		//... in registered models
		for each (var modelInstance:Object in controllerVo.models)
		{
			//start dependency rejection
			if(modelInstance && modelInstance.constructor)
			{
				propertyRejectionOfInstance(modelInstance.constructor, controllerVo, instance);
			}
		}
		
		//remove instance
		ControllerVO(_controller[ controller.getName() ]).models[modelName] = null;
		delete ControllerVO(_controller[ controller.getName() ]).models[modelName]; 
		
		log(controller.getName(), 'Model removed: ' +  modelName );
  	}
  	
	/**
	 * retrieves a registered Model 
	 * @param controller
	 * @param modelName
	 * @return 
	 * 
	 */  	
	public function retrieveModel(controller:Controller, modelName:String):IModel
	{	
		if(ControllerVO(_controller[ controller.getName() ]).models[modelName] == null)
		{
			throw new Error('model not registered: ' + modelName);
		}
		return ControllerVO(_controller[ controller.getName() ]).models[modelName];
	}
	
	/**
	 * registers the given view instance
	 * @param controllerName
	 * @param viewName
	 * @param viewInstance
	 * 
	 */		
	public function registerView(controllerName:String, viewName:String, viewInstance:Object):void
	{
		if( isViewRegistered(controllerName, viewName) )
		{
			throw new Error('view already registered: ' + viewName);
   		}

        //register Instance
        ControllerVO(_controller[ controllerName ]).views[ viewName ] = viewInstance;
           
        //start dependency Injection   
        dependencyInjection(_controller[ controllerName ] as ControllerVO);
            
      	//notify Application that view has registered
		retrieveController(controllerName).sendNotification(new NoteVO(Controller.VIEW_REGISTER, null, viewName, viewInstance) );
		
		//log		
		log(controllerName, 'View registered: ' +  viewName + ' ViewInstance: ' + viewInstance );
	}
	
	/**
	 * removes a registered View 
	 * @param controllerName
	 * @param viewName
	 * 
	 */	
	public function removeView(controllerName:String, viewName:String):void
	{
		if( !isViewRegistered(controllerName, viewName) )
		{
			throw new Error( "view not registered!");
		}
		//notify Application before view is removed
		retrieveController(controllerName).sendNotification( new NoteVO(Controller.VIEW_REMOVE, null, viewName, ControllerVO(_controller[ controllerName ]).views[viewName]) );	
		
		//clean up all dependencies...
		var controllerVo:ControllerVO = _controller[ controllerName ] as ControllerVO;
		var instance:Object = controllerVo.views[viewName];	
				
		rejectAllPropertiesInInstance(instance.constructor, controllerVo, instance);

		//remove mapped view
		ControllerVO(_controller[ controllerName ]).views[viewName] = null;
		delete ControllerVO(_controller[ controllerName ]).views[viewName];
		
		//log
		log(controllerName, 'Removed: View registered with ViewName: "' +  viewName + ' to controller: ' + controllerName);	
	}
	
	/**
	 * retrieves the registered view
	 * @param controllerName
	 * @param viewName
	 * @return 
	 * 
	 */		
	public function retrieveView(controllerName:String, viewName:String):Object
	{
		if( !isViewRegistered(controllerName, viewName) )
		{
			throw new Error( "view not registered!");   	
		} 	
		return ControllerVO(_controller[ controllerName ]).views[ viewName ];
	}
	
	
	/**
	 * dependency Injection to all registered views and models
	 * @param controller
	 * 
	 */	
	private function dependencyInjection(controllerVo:ControllerVO):void
	{
		//views
		for each (var viewInstance:Object in controllerVo.views)
		{
			 //start dependency injection
	        if(viewInstance && viewInstance.constructor)
	        {
	       	 	propertyInjection(viewInstance.constructor, controllerVo);
	        }
		}
		
		//models
		for each (var modelInstance:Object in controllerVo.models)
		{
			 //start dependency injection
	        if(modelInstance && modelInstance.constructor)
	        {
	       	 	propertyInjection(modelInstance.constructor, controllerVo);
	        }
		}
		
	}
	
	/**
	 * returns annotation notes by given annotation string. For Example 'Inject' 
	 * @param variableNodes
	 * @param accessorNodes
	 * @param annotation
	 * @return 
	 * 
	 */	
	private function returnAnnotationNodes(variableNodes:XMLList, accessorNodes:XMLList, annotation:String=CompactMVC.ANNOTATION_INJECT):Array
	{
		var arr:Array = [];
		var metaData:XMLList;
		
		//find name in variables
		for each (var nodeXML:XML in variableNodes) 
		{
			metaData = nodeXML.metadata;
			if(metaData.length())
			{
				if(String(nodeXML.metadata.((@name==annotation)).@name).length > 0)
					arr.push(nodeXML);
			}       	
		}
		
		//find name in accessores    
		for each (nodeXML in accessorNodes) 
		{	
			metaData = nodeXML.metadata;
			if(metaData.length())
			{
				if(String(nodeXML.metadata.((@name==annotation)).@name).length > 0)
					arr.push(nodeXML);
			}       	
		}
		
		return arr;
	}
	
	/**
	 * rejects all properites in View
	 * @param classRef
	 * @param controllerVo
	 * @param viewInstance
	 * 
	 */	
	private function rejectAllPropertiesInInstance(classRef:Class, controllerVo:ControllerVO, targetInstance:Object):void
	{
		if(!classRef)
			throw new Error('classRef cannot be null!');
		
		if(!targetInstance)
			throw new Error('instance cannot be null!');
		
		var classRefXML:XML = describeType(classRef);
		var nodeXML:XML;
		var variableName:String;
		var variableTypeClassName:String;
		var targetClassName:String;
		var modelName:String;
		var foundNodes:Array = returnAnnotationNodes(classRefXML.factory.variable, classRefXML.factory.accessor);
		
		if(!foundNodes || foundNodes.length == 0)
			return;
		
		//start rejection       
		for each(nodeXML in foundNodes)
		{
			variableName = nodeXML.@name;
			variableTypeClassName = nodeXML.@type;
			targetClassName	= nodeXML.@declaredBy;
			
			//find view with wanted ClassName
			if(getQualifiedClassName(targetInstance) == targetClassName)
			{
				//reject model		    				 				
				targetInstance[variableName] = null;
				
				//log		
				log(controllerVo.controller.getName(), '## clean up Dependencies in Instance ## -> ApplicationController [' + getQualifiedClassName(controllerVo.controller) + '] targetInstance ['+targetInstance+'] targetType [' + targetClassName + '] variableName [' + variableName + '] variableType [' + variableTypeClassName + ']' );
			}	
		}
	}
		
	
	/**
	 * rejects the given property instance in all registered views and models (DI)
	 * @param classRef
	 * @param controllerVo
	 * @param instance
	 * 
	 */	
	private function propertyRejectionOfInstance(classRef:Class, controllerVo:ControllerVO, instance:Object):void
	{
		if(!classRef)
			throw new Error('classRef cannot be null!');
		
		if(!instance)
			throw new Error('instance cannot be null!');
		
		var instanceClassName:String = getQualifiedClassName(instance);
		
		var classRefXML:XML = describeType(classRef);
		//var methodNodes:XMLList = classRefXML.factory.method;
		var nodeXML:XML;
		var variableName:String;
		var variableTypeClassName:String;
		var targetClassName:String;
		var viewName:String;
		var modelName:String;
		var controllerVo:ControllerVO;
		var targetInstance:Object;
		var foundNodes:Array = returnAnnotationNodes(classRefXML.factory.variable, classRefXML.factory.accessor);
			
		if(!foundNodes || foundNodes.length == 0)
			return;
		
		//start rejection       
		for each(nodeXML in foundNodes)
		{
			variableName = nodeXML.@name;
			variableTypeClassName = nodeXML.@type;
			targetClassName	= nodeXML.@declaredBy;
			
			//iterate through the view list (registered views with controller)
			for (viewName in controllerVo.views)
			{
				//retrieve viewInstance
				targetInstance = retrieveView(controllerVo.controller.getName(), viewName);
				
				//find view with wanted ClassName
				if(getQualifiedClassName(targetInstance) == targetClassName && variableTypeClassName == instanceClassName)
				{
					//reject model		    				 				
					targetInstance[variableName] = null;
					
					//log		
					log(controllerVo.controller.getName(), '## view Dependency Rejection for Instance (' + instanceClassName + ') ## -> ApplicationController [' + getQualifiedClassName(controllerVo.controller) + '] targetInstance ['+targetInstance+'] targetType [' + targetClassName + '] variableName [' + variableName + '] variableType [' + variableTypeClassName + ']' );
				}
			}
			
			//iterate through the model list (registered models with controller)
			for (modelName in controllerVo.models)
			{				
				//retrieve model Instance
				targetInstance = retrieveModel(controllerVo.controller, modelName);
				
				//find view with wanted ClassName
				if(getQualifiedClassName(targetInstance) == targetClassName && variableTypeClassName == instanceClassName)
				{
					//reject model		    				 				
					targetInstance[variableName] = null;
					
					//log		
					log(controllerVo.controller.getName(), '## model Dependency Rejection for Instance (' + instanceClassName + ')## -> ApplicationController [' + getQualifiedClassName(controllerVo.controller) + '] targetInstance ['+targetInstance+'] targetType [' + targetClassName + '] variableName [' + variableName + '] variableType [' + variableTypeClassName + ']' );
				}
			}
				    
		}
	}
	
	/**
	 * inject property instances having set metadata tag annotations to [Inject]
	 * @param myClass
	 * @param metadataTagName
	 * 
	 */	
	private function propertyInjection(classRef:Class, controllerVo:ControllerVO, commandInstance:ICommand=null):void
	{
		if(!classRef)
			throw new Error('classRef cannot be null!')
			
		var classRefXML:XML = describeType(classRef);
		//var methodNodes:XMLList = classRefXML.factory.method;
		var nodeXML:XML;
        var variableName:String;
        var variableTypeClassName:String;
        var targetClassName:String;
        var viewName:String;
        var controllerVo:ControllerVO;
        var modelName:String;
        var instanceToInject:Object;
        var targetInstance:Object
        var commandName:String;		
		var foundNodes:Array = returnAnnotationNodes(classRefXML.factory.variable, classRefXML.factory.accessor);
		
                
        if(!foundNodes || foundNodes.length == 0)
        	return;
        	    
		//start injection       
        for each(nodeXML in foundNodes)
        {
        	variableName = nodeXML.@name;
        	variableTypeClassName = nodeXML.@type;
        	targetClassName	= nodeXML.@declaredBy;
        	
        	//try modelinstance to inject
        	instanceToInject = retrieveModelByClassName(variableTypeClassName);	
        	
        	/*
        	* to do:
        	 * viewinstance injection will not work here, because there is no unique identifier accessible
        	 */
        	 
        	//try viewinstance to inject
        	//if(!instanceToInject)
        		//instanceToInject = retrieveViewByClassName(variableTypeClassName);	
        	
        	//break if instance is null
        	if(!instanceToInject || !(instanceToInject is IModel))
        		continue;
        		
    		//command
    		if(commandInstance && commandInstance is ICommand)
    		{			
    			//find view with wanted ClassName
    			//if(getQualifiedClassName(commandInstance) == targetClassName)
				if(getDefinitionByName(getQualifiedClassName(commandInstance)) == classRef)
    			{
    				//inject model instance	    				 				
    				commandInstance[variableName] = instanceToInject;
    				
    				//log		
					log(controllerVo.controller.getName(), '## command Dependency Injection ## -> ApplicationController [' + getQualifiedClassName( controllerVo.controller ) + '] targetInstance ['+commandInstance+'] targetType [' + targetClassName + '] variableName [' + variableName + '] variableType [' + variableTypeClassName + ']' );
    			}
    		}
    		else
    		{
	    		//iterate through the view list (registered views with controller)
	    		for (viewName in controllerVo.views)
	    		{
	    			//retrieve viewInstance
	    			targetInstance = retrieveView(controllerVo.controller.getName(), viewName);
	    			
	    			//find view with wanted ClassName
	    			//if(getQualifiedClassName(targetInstance) == targetClassName)
					if(getDefinitionByName(getQualifiedClassName(targetInstance)) == classRef)
					{
	    				//inject model		    				 				
	    				targetInstance[variableName] = instanceToInject;
	    				//log		
						log(controllerVo.controller.getName(), '## view Dependency Injection ## -> ApplicationController [' + getQualifiedClassName(controllerVo.controller) + '] targetInstance ['+targetInstance+'] targetType [' + targetClassName + '] variableName [' + variableName + '] variableType [' + variableTypeClassName + ']' );
	    			}
	    		}
	    		
	    		//iterate through the model list (registered models with controller)
	    		for (modelName in controllerVo.models)
	    		{
	    			//retrieve model Instance
	    			targetInstance = retrieveModel(controllerVo.controller, modelName);
	    			
	    			//find view with wanted ClassName
	    			//if(getQualifiedClassName(targetInstance) == targetClassName)
					if(getDefinitionByName(getQualifiedClassName(targetInstance)) == classRef)	    			
					{
	    				//inject model		    				 				
	    				targetInstance[variableName] = instanceToInject;
	    				//log		
						log(controllerVo.controller.getName(), '## model Dependency Injection ## -> ApplicationController [' + getQualifiedClassName(controllerVo.controller) + '] targetInstance ['+targetInstance+'] targetType [' + targetClassName + '] variableName [' + variableName + '] variableType [' + variableTypeClassName + ']' );
	    			}
	    		}
    		}	    
        }
	}
	
	/**
	 * returns a registered model instance by  className, null if nothing found
	 * @param className
	 * @return 
	 * 
	 */	
	public function retrieveModelByClassName(className:String):IModel
	{	
		var controllerVo:ControllerVO;
		var modelInstance:IModel
		
		for each(controllerVo in _controller)
		{
		 	for each (modelInstance in controllerVo.models)
		    {
		    	if(getQualifiedClassName(modelInstance) == className)
		    	{
		    		return modelInstance;
		    	}
		    }
		}
		return null;
	}
	
	/**
	 *  returns a registered view instance by className, null if nothing found
	 * @param className
	 * @return 
	 * 
	 */	
	public function retrieveViewByClassName(className:String):DisplayObject
	{	
		var controllerVo:ControllerVO;
		var viewInstance:DisplayObject
		
		for each(controllerVo in _controller)
		{
		 	for each (viewInstance in controllerVo.views)
		    {
		    	if(getQualifiedClassName(viewInstance) == className)
		    	{
		    		return viewInstance;
		    	}
		    }
		}
		return null;
	}
	
	/**
	 * resets a controller 
	 * @param controllerName
	 * 
	 */	
	public function cleanup(controllerName:String):void
	{
		var controllerVo:ControllerVO = _controller[controllerName];

		//remove all views
		for (var viewKey:String in controllerVo.views)
		{
			removeView(controllerName, viewKey);
		}
				
		//remove all Models
		for (var modelKey:String in controllerVo.models)
		{
			removeModel(controllerVo.controller, modelKey);
		}
		//remove all Commands
		for (var commandKey:String in controllerVo.commands)
		{
			removeCommand(controllerVo.controller, commandKey);
		}
		
		//remove Controller
		removeController(controllerVo.controller);
		
		//notify Application
		CompactMVC.getInstance().sendJunctionNotification(Controller.CONTROLLER_REMOVE, controllerName);
		
		
		//log status
		logStatus(controllerName, controllerVo);
	}
	
	/**
	 * checks whether a controller is present 
	 * @param controllerName
	 * @return 
	 * 
	 */		
	public function isControllerRegistered(controllerName:String):Boolean
	{
		if(_controller[ controllerName ] != null)
		{
			return true;
		}
		return false;
	}
	
	/**
	 * checks whether a model is present  
	 * @param controllerName
	 * @param modelName
	 * @return 
	 * 
	 */		
	public function isModelRegistered(controllerName:String, modelName:String):Boolean
	{
		if( isControllerRegistered(controllerName) )
		{
			if(ControllerVO(_controller[ controllerName ]).models[ modelName ] != null)
			{
				return true;
			}
		}			
		return false;
	}
	
	/**
	 * checks whether a view is present  
	 * @param controllerName
	 * @param viewName
	 * @return 
	 * 
	 */		
	public function isViewRegistered(controllerName:String, viewName:String):Boolean
	{
		if( isControllerRegistered(controllerName) )
		{
			if(ControllerVO(_controller[ controllerName ]).views[viewName] != null)
			{
				return true;
			}
		}
		return false;
	}
	
	/**
	 * retrieves all registered Views of given controller 
	 * @param controllerName
	 * @return 
	 * 
	 */	
	public function retrieveAllRegisteredViews(controllerName:String):Array
	{
		if( isControllerRegistered(controllerName) )
		{
			if(ControllerVO(_controller[ controllerName ]).views != null)
			{
				return ControllerVO(_controller[ controllerName ]).views;
			}
		}
		return null;
	}
	
	
	
	/**
	 * trace log message 
	 * @param controllerName
	 * @param message
	 * 
	 */	
	private function log(controllerName:String, message:String):void
	{
		if(!isControllerRegistered(controllerName))
			return;
			
		switch( ControllerVO(_controller[ controllerName ]).controller.logLevel)
		{
			case CompactMVC.LOGLEVEL_ALL:		
				logStatus(controllerName, ControllerVO(_controller[ controllerName ]));
			case CompactMVC.LOGLEVEL_DEBUG:
				logMessage(ControllerVO(_controller[ controllerName ]).controller, message);
		}
	}
	
	/**
	 * trace log message
	 * @param controller
	 * @param message
	 * 
	 */	
	private function logMessage(controller:Controller, message:String):void
	{	
		var msg:String = currentTime + ' ' + message;	
		if(controller.logFunction != null)
			controller.logFunction(message);
		else
			trace(msg);
	}
	
	/**
	 * trace the controller status 
	 * @param controllerName
	 * @param controllerVO
	 * 
	 */	
	private function logStatus(controllerName:String, controllerVO:ControllerVO):void
	{
		logMessage(controllerVO.controller, '');
 		logMessage(controllerVO.controller, '[ -- DETAILED LOG START -- ]');
		logMessage(controllerVO.controller, '[ -- Controller Name: ' + controllerName + '	[' + getQualifiedClassName(controllerVO.controller)  + ']');
		
		//list commands
		logMessage(controllerVO.controller, '     Commands ------------------------->');
		for (var commandKey:String in controllerVO.commands)
		{
			logMessage(controllerVO.controller, '	Notification Type: '+ commandKey + '	[' + getQualifiedClassName( controllerVO.commands[commandKey]) + ']'); 
		}
		
		//list models
		logMessage(controllerVO.controller, '     Models --------------------------->');
		for (var modelKey:String in controllerVO.models)
		{
			logMessage(controllerVO.controller, '	Model Name: ' + modelKey + '	[' +  getQualifiedClassName( controllerVO.models[modelKey]) + ']' ); 
		}
		
		//list views
		logMessage(controllerVO.controller, '     Views ---------------------------->');
		for (var viewKey:String in controllerVO.views)
		{
			logMessage(controllerVO.controller, '	View Name: ' + viewKey + ' [' + getQualifiedClassName( controllerVO.views[viewKey]) + ']' );
		}
		
		logMessage(controllerVO.controller, ']');		
		logMessage(controllerVO.controller, '[ -- DETAILED LOG STOP -- ]');
		logMessage(controllerVO.controller, '');
	}
	
	/**
	 * Creates a String of valid time value
	 * @return 			String 		current time as a String using valid hours, minutes, seconds and milliseconds
	 */	 
	private function get currentTime ():String
    {
		var date:Date = new Date();
		return "CompactMVC " + fixTime(date.getHours()) + ':' + fixTime(date.getMinutes()) + ':' + fixTime(date.getSeconds()) + '.' + fixTime(date.getMilliseconds());
    }
    
    private function fixTime(value:Number):String
    {
    	return value > 9 ? value.toString() : "0" + value.toString();
    }
}

class SingletonEnforcer {}
